<template>
<!--  <GoBack />-->
  <a-row justify="center">
    <a-col :xs="22" :lg="16" :xl="15" :xxl="13">
      <div class="go-back" @click="goBack"><double-left-outlined /><span>&nbsp;返回</span></div>
    </a-col>
  </a-row>
  <div>
    <div class="header">
      <a-row justify="center">
        <a-col :xs="22" :lg="16" :xl="15" :xxl="13">
          <div class="subject" :style="{ backgroundColor: subject.color }">
            <span>{{ subject.name }}</span>
          </div>
        </a-col>
      </a-row>
    </div>
    <div class="navi">
      <a-row justify="center">
        <a-col :xs="22" :lg="16" :xl="15" :xxl="13">
          <a-row justify="center">
            <a-col :xs="8">
              <div class="sub not-select">
                <span @click="switchCate(-1)">{{preCate}}</span>
              </div>
            </a-col>
            <a-col :xs="8">
              <div class="sub selected">
                <span>{{curCate}}</span>
              </div>
            </a-col>
            <a-col :xs="8">
              <div class="sub not-select">
                <span @click="switchCate(1)">{{ postCate }}</span>
              </div>
            </a-col>
          </a-row>
        </a-col>
      </a-row>
    </div>
    <a-divider><fire-filled style="color: red; font-size: 16px"/></a-divider>
    <div>
      <a-row justify="center">
        <a-col :xs="22" :lg="16" :xl="15" :xxl="13">
          <div class="problem-pad" v-show="cateQusNum > 0">
            <div class="pad-header">
              <span>{{ qusIdx + 1 }} / {{ cateQusNum }}</span>
              <span class="op op-color" @click="switchQus(-1)">上一题</span>
              <span class="op op-color" @click="switchQus(1)"><loading-outlined style="font-size: 0.9em" v-show="switchLoading"/>下一题</span>
              <span class="op" @click="showAns" :class="{ 'disabled': !enabledShowAns, 'op-color': enabledShowAns}">解析</span>
              <star-filled v-if="showCollect" @click="addCollect" style="margin-left: 15px;color: #FFBD33;" v-show="collectStatus"/>
              <star-outlined v-if="showCollect" @click="addCollect" style="margin-left: 15px; color: #868383;" v-show="!collectStatus"/>
            </div>
            <div class="problem" :style="{ boxShadow: ansResultColor }">
              <div class="qus-info">
                <a-tag color="purple">{{ qus.type }}</a-tag>
                <a-tag color="green" v-if="qus.type !== '0'">{{ qus.year }} 第{{ qus.number }}题</a-tag>
                <check-circle-filled style="color: #2ecc71"/>
                <span>{{ qus.acc }}%</span>
                <edit-filled style="color: #34495e"/>
                <span>{{ qus.try }}</span>
              </div>
              <div class="qus-info">
                <a-tag color="cyan" v-for="(v, idx) in qus.tag" :key="idx">{{v}}</a-tag>
              </div>
              <ShowProblem :content="qus.question"/>
              <div class="choice">
                <a-radio-group button-style="solid" v-model:value="curChoice" @change="onChoiceChange($event, qus.id)">
                  <a-radio-button value="A">A</a-radio-button>
                  <a-radio-button value="B">B</a-radio-button>
                  <a-radio-button value="C">C</a-radio-button>
                  <a-radio-button value="D">D</a-radio-button>
                </a-radio-group>
              </div>
            </div>
            <a-divider><bulb-filled style="font-size: 1.5em; color: #f1c40f"/></a-divider>
            <div v-show="isShowAns">
              <div>
                <a-button type="link" :href="`/user/answer/${qus.id}`" target="_blank">写答案</a-button>
<!--                <a-button type="link">记笔记</a-button>-->
<!--                <a-button type="link">看笔记</a-button>-->
              </div>
            </div>
            <div class="answer" v-for="(ans, idx) in qus.answer" :key="idx" v-show="isShowAns">
              <div class="provider">
<!--                <copyright-circle-outlined style="color: #9b59b6"/>-->
                <span>{{ ans.author }}</span>
              </div>
              <ShowProblem :content="ans.content"/>
            </div>
          </div>
          <div v-show="isShowAns" style="margin-top: 50px">
            <a-divider><comment-outlined style="font-size: 1.5em; color: #45e87f"/></a-divider>
            <OuterComment :uname="uname" :pid="qus.id" ref="commentPad"></OuterComment>
          </div>
        </a-col>
      </a-row>
    </div>
    <div class="footer">
      <span style="font-family:arial,serif;">Copyright &copy; 2022 Just for fun.</span>
    </div>
  </div>
</template>

<script lang="ts">
import { Col, Divider, message, Radio, Row, Tag, Tooltip, Button } from 'ant-design-vue'
import {
  BulbFilled,
  CheckCircleFilled,
  CommentOutlined,
  CopyrightCircleOutlined,
  DoubleLeftOutlined,
  EditFilled,
  FireFilled,
  LoadingOutlined,
  StarFilled,
  StarOutlined
} from '@ant-design/icons-vue'
import { defineComponent, onBeforeUnmount, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
  Breakpoint,
  Collect,
  createDefaultQusItem,
  HistoryAnswer,
  KillRecord,
  QusItem,
  SolveLog,
  transUrl
} from '@/utils/ds'
import {
  addBreakpoint,
  addRecordLog,
  addUserCollect,
  deleteCollect,
  getCollectBySub,
  getSubjectCategories,
  getSubjectCategoryId,
  getTargetQus,
  getUserHistoryAnswer,
  setUserHistoryAnswer
} from '@/service'
import { checkToken, decodeCT, decodeID, decodeValue, getUserName, subjectCode2Name } from '@/utils/check'
import { AnsFieldSeq, AnsSeq, BREAKPOINT_KEY, CACHED_CHOICE, SESSION_NEW_COLLECT, USER_INFO_KEY } from '@/utils/constants'
import ShowProblem from '@/components/ShowProblem.vue'
import OuterComment from '@/components/OuterComment.vue'
import {
  checkUserUpdateExpire,
  getCache,
  getCacheLong,
  getSubjectCategoryCode,
  getSubjectColor,
  randInt,
  setCache,
  setCacheLong,
  setCacheLongWithTime
} from '@/utils/common'
import { murmur2 } from 'murmurhash2'

interface Piece<T> {
  g: number,
  data: T[]
}

interface CollectTuple {
  id: number,
  pid: number
}

interface SubjectCate {
  name: string,
  ids: Map<number, Piece<number>> | null,
  total: number
}

interface Subject {
  name: string,
  color: string,
  category: SubjectCate[],
  code: string
}

interface RawQus {
  ct: string,
  rt: string,
  lk: string
}

interface ChoicePair {
  choice: string,
  created: number,
  status: number
}

interface UserInfo {
  name: string,
  acc: number,
  rank: number,
  kickNumber: number,
  rankClass: string,
  kickProgress: number,
  totalQusNum: number
}

export default defineComponent({
  name: 'SolveProblem',
  components: {
    ShowProblem,
    FireFilled,
    LoadingOutlined,
    CommentOutlined,
    DoubleLeftOutlined,
    StarFilled,
    StarOutlined,
    CheckCircleFilled,
    EditFilled,
    BulbFilled,
    // CopyrightCircleOutlined,
    ARow: Row,
    ACol: Col,
    ADivider: Divider,
    ATag: Tag,
    ARadioGroup: Radio.Group,
    ARadioButton: Radio.Button,
    AButton: Button,
    // GoBack,
    OuterComment
  },
  setup () {
    const router = useRouter()
    const route = useRoute()
    const commentPad = ref()
    const loadedQusBatchSize = 8
    let randQusCate = 'rnd_0'

    const uname = ref<string>('K')

    const collectStatus = ref<boolean>(false)
    const showCollect = ref<boolean>(true)
    // 当前展示问题相关数据结构
    const qus = ref<QusItem>(createDefaultQusItem())
    const curChoice = ref<string>('')
    const isShowAns = ref<boolean>(false)
    const enabledShowAns = ref<boolean>(false)
    const switchLoading = ref<boolean>(false)

    const subject = ref<Subject>({
      name: 'Unknown',
      code: 'X',
      color: '#3498db',
      category: []
    })
    const preCate = ref<string>('')
    const curCate = ref<string>('')
    const postCate = ref<string>('')
    const cateIdx = ref<number>(-1)
    const cateQusNum = ref<number>(0)
    const qusIdx = ref<number>(-1)

    const killRecord = ref<Map<number, KillRecord>>(new Map<number, KillRecord>())
    const curCateLoadedQus = reactive<Map<number, Piece<RawQus>>>(new Map<number, Piece<RawQus>>())
    const cachedChoice = ref<Map<number, ChoicePair>>(new Map<number, ChoicePair>())
    const collectSet = ref<Map<number, number>>(new Map<number, number>())

    const ansRightColor = ref<string>('rgba(20, 220, 53, 0.8) 0 3px 8px')
    const ansWrongColor = ref<string>('rgba(215, 26, 26, 0.5) 0 3px 8px')
    const ansResultColor = ref<string>('')
    const lastSwitchQusTime = ref<number>(0)

    const switchCate = (diff: number) => {
      const t = cateIdx.value + diff
      if (setupCategory(t)) {
        loadCurCateQus(0)
      }
    }

    const switchQus = (diff: number) => {
      if (cateQusNum.value <= 1) {
        return
      }
      const t = qusIdx.value + diff
      if (t >= cateQusNum.value || t < 0) {
        return
      }

      const old = lastSwitchQusTime.value
      const cur = new Date().getTime()
      if (old === 0 || (cur > old && cur - old > 600)) {
        // 加载问题
        loadCurCateQus(t)
        lastSwitchQusTime.value = cur
        return
      }

      const sleepTime = 500
      if (diff > 0 && !switchLoading.value) {
        switchLoading.value = true
        setTimeout(() => {
          // 加载问题
          loadCurCateQus(t)

          switchLoading.value = false
        }, sleepTime)
      }
    }

    const resetQusState = () => {
      curChoice.value = ''
      isShowAns.value = false
      enabledShowAns.value = false
      ansResultColor.value = ''
      collectStatus.value = false
    }

    const rebuildQusState = () => {
      // 判断是否已经选择
      const pid = qus.value.id
      if (pid === 0) {
        return
      }

      // 恢复答案
      const s = killRecord.value.get(pid)
      if (s !== undefined) {
        curChoice.value = s.select
        enabledShowAns.value = true
        if (s.status === 1) {
          ansResultColor.value = ansRightColor.value
        } else {
          ansResultColor.value = ansWrongColor.value
        }
      } else {
        const choice = cachedChoice.value.get(pid)
        if (choice !== undefined) {
          curChoice.value = choice.choice
          enabledShowAns.value = true
        }
      }

      // 恢复收藏状态
      const ecid = collectSet.value.get(-pid)
      if (ecid !== undefined) {
        if (ecid > 0) {
          collectStatus.value = true
        }
      } else {
        const time = collectSet.value.get(pid)
        if (time !== undefined && time > 0) {
          collectStatus.value = true
        }
      }
    }

    const setupPlayground = async (sub: string) => {
      let bkFlag = false
      let curSubject = sub
      if (sub.startsWith('bk')) {
        bkFlag = true
        const i = sub.indexOf('_')
        curSubject = sub.substring(i + 1)
      }

      curSubject = curSubject.toUpperCase()
      if (curSubject === 'FUN' || curSubject === 'SEQ') {
        showCollect.value = false
      }
      // 设置学科信息
      await setupSubjectCate(curSubject)

      let targetCateIdx = 0
      let targetCateQusIdx = 0

      // 恢复断点
      if (bkFlag && curSubject !== 'FUN') {
        const cached = getCache(BREAKPOINT_KEY)
        if (cached.length > 0) {
          const d = JSON.parse(cached) as Breakpoint[]
          let bkCate = ''
          let bkQusIdx = -1
          for (let i = 0; i < d.length; i++) {
            if (curSubject === d[i].subject) {
              bkCate = d[i].category
              bkQusIdx = d[i].idx
              break
            }
          }
          if (bkCate !== '') {
            const bkCateIdx = subject.value.category.findIndex(v => {
              return v.name === bkCate
            })
            if (bkCateIdx !== -1) {
              targetCateIdx = bkCateIdx
              targetCateQusIdx = bkQusIdx
            }
          }
        }
      }

      setupCategory(targetCateIdx)
      await loadCurCateQus(targetCateQusIdx)
    }

    const setupSubjectCate = async (sub: string): Promise<boolean> => {
      if (sub.length === 0) {
        message.error('出错了！')
        return false
      }
      const SUBJECT_KEY = sub.toUpperCase()
      const candidate = ['CS', 'CCP', 'NET', 'OS', 'SEQ', 'FUN']
      if (candidate.indexOf(SUBJECT_KEY) === -1) {
        message.error('未知科目')
        return false
      }

      subject.value.color = '#' + getSubjectColor(SUBJECT_KEY)
      subject.value.name = subjectCode2Name(SUBJECT_KEY)
      subject.value.code = SUBJECT_KEY
      // 如果是随机刷题则不需要加载
      if (SUBJECT_KEY === 'FUN') {
        randQusCate = `rnd_${randInt(0, 20)}`
        subject.value.category.push({
          name: '随机刷题',
          ids: null,
          total: 40
        })
        return true
      }

      // const cacheKey = `${SUBJECT_KEY}_CATE`
      const cacheKey = murmur2(`${SUBJECT_KEY}_CATE`, 8989).toString(36)
      const cached = getCacheLong(cacheKey)
      // 有缓存
      if (cached.length > 0) {
        const targetData = JSON.parse(cached)
        targetData.forEach((v: string) => {
          const fields = v.split(',')
          subject.value.category.push({
            name: fields[0],
            ids: null,
            total: parseInt(fields[1])
          })
        })
        return true
      }

      // 本地无缓存 请求远程
      const response = await getSubjectCategories(SUBJECT_KEY)
      if (response.status !== 200) {
        return false
      }
      const resp = response.data
      if (resp.code !== 0 || resp.data == null) {
        message.error(resp.msg, 0.5)
        return false
      }
      const data = resp.data as Array<string>
      const subjectCategories:SubjectCate[] = []
      let targetData = data
      if (SUBJECT_KEY === 'SEQ') {
        targetData = data.sort()
      }
      targetData.forEach(v => {
        const fields = v.split(',')
        subjectCategories.push({
          name: fields[0],
          ids: null,
          total: parseInt(fields[1])
        })
      })
      subject.value.category = subjectCategories
      setCacheLong(cacheKey, JSON.stringify(targetData))
      return true
    }

    const setupCategory = (idx: number): boolean => {
      const sub = subject.value
      const cate = sub.category
      if (idx < 0 || idx >= cate.length) {
        // message.warn('超出检索范围')
        return false
      }

      const targetCate = cate[idx]
      if (targetCate.total === 0) {
        message.info('该类别还没有题目', 0.2)
        // return false
      }

      // 重置信息
      preCate.value = ''
      curCate.value = ''
      postCate.value = ''
      cateIdx.value = -1
      cateQusNum.value = 0

      curCate.value = cate[idx].name

      if (idx > 0) {
        preCate.value = cate[idx - 1].name
      }

      if (idx < cate.length - 1) {
        postCate.value = cate[idx + 1].name
      }

      // 设置当前类别指针
      cateIdx.value = idx

      // 设置当前类别题目总数
      cateQusNum.value = targetCate.total

      // 设置当前类别题目加载指针
      qusIdx.value = -1

      // 清空已经加载的题目
      curCateLoadedQus.clear()
      return true
    }

    const loadCurCateQus = async (queryQusIdx: number) => {
      const curCateIdx = cateIdx.value
      qusIdx.value = queryQusIdx
      if (curCateIdx < 0 || curCateIdx >= subject.value.category.length) {
        return
      }
      const sub = subject.value
      const targetCate = sub.category[curCateIdx]
      // 已经加载所有问题
      if (queryQusIdx >= targetCate.total) {
        return
      }

      const group = Math.floor(queryQusIdx / loadedQusBatchSize)
      const index = queryQusIdx % loadedQusBatchSize

      // 检查问题是否已经加载
      if (!curCateLoadedQus.has(group)) {
        if (targetCate.ids === null) {
          targetCate.ids = new Map<number, Piece<number>>()
        }

        if (!targetCate.ids.has(group)) {
          // 从缓存恢复
          const cateName = targetCate.name
          // const cachedKey = `${sub.code}_${cateName}_${group}`
          const cachedKey = murmur2(`${sub.code}_${cateName}_${group}`, 8919).toString(36)
          const cached = getCacheLong(cachedKey)
          if (cached.length > 0) {
            const d = JSON.parse(cached)
            if (checkUserUpdateExpire(d.time)) {
              const piece: Piece<number> = {
                g: group,
                data: []
              }
              piece.data = d.data
              targetCate.ids.set(group, piece)
            }
          }

          if (!targetCate.ids.has(group)) {
            // 从网络加载
            const reqCateID = await loadSubjectCateId(sub.code, cateName, group * loadedQusBatchSize, loadedQusBatchSize)
            const piece: Piece<number> = {
              g: group,
              data: reqCateID
            }
            targetCate.ids.set(group, piece)
            if (sub.code !== 'FUN') {
              setCacheLongWithTime(cachedKey, reqCateID)
            }
          }
        }

        const cateGroupId = targetCate.ids.get(group)
        // 触发加载问题
        if (cateGroupId !== undefined) {
          const response = await getTargetQus(cateGroupId.data)
          if (response.status === 200) {
            const resp = response.data
            if (resp.code === 0 && resp.data !== null) {
              const qusPiece: Piece<RawQus> = {
                g: group,
                data: resp.data as RawQus[]
              }
              curCateLoadedQus.set(group, qusPiece)
            }
          }
        }
      }

      const targetQus = curCateLoadedQus.get(group)
      if (targetQus !== undefined) {
        const rawQusList = targetQus.data
        if (index >= 0 && index < rawQusList.length) {
          const curRawQus = rawQusList[index]
          renderQus(curRawQus)
        }
      }
    }

    const loadSubjectCateId = async (
      sub: string, cateName: string,
      start: number, count: number): Promise<number[]> => {
      if (sub === 'FUN') {
        cateName = randQusCate
      }
      const response = await getSubjectCategoryId(sub, cateName, start, count)
      if (response.status !== 200) {
        return []
      }
      const resp = response.data
      if (resp.code !== 0 || resp.data === null) {
        return []
      }
      const lk = resp.data.lk as string
      const ct = resp.data.ct as string
      const k = decodeID(lk)
      if (k !== 0) {
        const idString = decodeValue(ct, k)
        const ids = idString.split('.')
        return ids.map(v => {
          return parseInt(v)
        })
      }
      return []
    }

    const renderQus = (qusRaw: RawQus) => {
      const key = decodeID(qusRaw.lk)
      const header = decodeValue(qusRaw.rt, key)
      const content = decodeCT(header, qusRaw.ct, key)
      if (content === undefined) {
        return
      }
      // 初始化与问题相关的状态
      resetQusState()

      const e = qus.value
      let v = content.get('id')
      e.id = v === undefined ? 0 : parseInt(v)

      v = content.get('year')
      e.year = v === undefined ? '' : v

      v = content.get('number')
      e.number = v === undefined ? 1 : parseInt(v)

      v = content.get('question')
      if (v !== undefined) {
        v = transUrl(v)
        e.question = v
      } else {
        e.question = ''
      }

      v = content.get('tag')
      e.tag = v === undefined ? [] : v.split('#')

      v = content.get('type')
      e.type = v === undefined ? '408' : v
      if (e.type === '0') {
        e.type = '其他'
      }

      v = content.get('subject')
      e.subject = v === undefined ? 'Unknown' : v

      v = content.get('choice')
      e.choice = v === undefined ? 'X' : v

      v = content.get('answer')
      if (v !== undefined) {
        v = transUrl(v)
        const ansList = v.split(AnsSeq)
        e.answer.length = 0
        ansList.forEach(value => {
          const a = value.split(AnsFieldSeq)
          if (!a[0].startsWith('###### 答案')) {
            a[0] = '###### 答案:' + e.choice + '\n\n' + a[0]
          }
          e.answer.push({ content: a[0], author: a[1] })
        })
      } else {
        e.answer = []
      }

      v = content.get('acc')
      e.acc = v === undefined ? 0 : parseInt(v)

      v = content.get('try_')
      e.try = v === undefined ? 0 : parseInt(v)

      rebuildQusState()
    }

    const showAns = () => {
      if (!enabledShowAns.value) {
        message.info('请先选择答案')
        return
      }

      if (isShowAns.value) {
        return
      }

      isShowAns.value = true

      const ans = qus.value.choice.toUpperCase()
      if (ans === 'X') {
        return
      }

      if (curChoice.value === ans) {
        ansResultColor.value = ansRightColor.value
      } else {
        ansResultColor.value = ansWrongColor.value
      }
      commentPad.value.loadQusComment()

      const h = killRecord.value
      const id = qus.value.id
      if (!h.has(id)) {
        const c = curChoice.value
        const tc = qus.value.choice
        const current = new Date().getTime()
        h.set(id, { select: c, status: c === tc ? 1 : 0, pid: id, created: current })
      }
    }

    const loadUserCollect = (sub: string) => {
      let curSubject = sub
      if (sub.startsWith('bk')) {
        const i = sub.indexOf('_')
        curSubject = sub.substring(i + 1)
      }
      curSubject = curSubject.toUpperCase()
      if (curSubject === 'FUN' || curSubject === 'SEQ') {
        return
      }
      const cachedKey = murmur2(`coll_${curSubject}`, 8989).toString(36)
      const cached = getCache(cachedKey)
      if (cached.length > 0) {
        const d = JSON.parse(cached)
        for (let i = 0; i < d.length; i++) {
          if (d[i].id > 0) {
            collectSet.value.set(d[i].pid, d[i].id)
          }
        }
        // console.log(collectSet.value)
        return
      }

      const cate = getSubjectCategoryCode(curSubject)
      getCollectBySub(cate)
        .then(response => {
          if (response.status === 200) {
            const resp = response.data
            if (resp.code === 0 && resp.data !== null) {
              const collect: CollectTuple[] = []
              resp.data.forEach((item: CollectTuple) => {
                // pid -> question id   id -> collect record id
                const e:CollectTuple = {
                  id: item.id,
                  pid: -item.pid
                }
                collect.push(e)
                collectSet.value.set(e.pid, e.id)
              })
              setCache(cachedKey, JSON.stringify(collect))
            }
          }
        })
    }

    const addCollect = () => {
      const origin = collectStatus.value
      collectStatus.value = !collectStatus.value
      const pid = qus.value.id
      if (pid === 0) {
        return
      }
      if (origin) {
        // 取消收藏
        const cid = collectSet.value.get(-pid)
        if (cid !== undefined) {
          deleteCollect(cid)
          collectSet.value.set(-pid, 0)
        } else {
          collectSet.value.set(pid, 0)
        }
      } else {
        // 添加收藏
        collectSet.value.set(pid, new Date().getTime())
      }
    }

    const leaveSubmitCollect = () => {
      // collect category 1 => CS 2 => CCP 3 => OS 4 => NET
      const s = collectSet.value
      const token = checkToken()
      const uid = decodeID(token)
      if (uid === 0) {
        return
      }
      const d:Collect[] = []
      const c = getSubjectCategoryCode(subject.value.code)
      const cacheCollect: CollectTuple[] = []
      // k -> pid v -> timestamp
      s.forEach((v, k) => {
        if (v <= 0) {
          return
        }
        // 历史收藏
        if (k < 0) {
          cacheCollect.push({
            id: v,
            pid: k
          })
        }
        // 新增的收藏
        if (k > 0) {
          d.push({
            pid: k,
            created: v,
            category: c
          })
        }
      })

      const cachedKey = murmur2(`coll_${subject.value.code}`, 8989).toString(36)
      if (d.length > 0) {
        addUserCollect(d)
          .then((response) => {
            if (response.status !== 200) {
              return
            }
            const resp = response.data
            if (resp.code !== 0 || resp.data === null) {
              return
            }
            const newCollectRecord:{id: number, pid: number, time: string}[] = []
            resp.data.forEach((v: any) => {
              cacheCollect.push({
                id: v.id,
                pid: -v.pid
              })

              newCollectRecord.push({
                id: v.id,
                pid: v.pid,
                time: v.created
              })
            })
            if (newCollectRecord.length > 0) {
              setCache(SESSION_NEW_COLLECT, JSON.stringify(newCollectRecord))
            }
            setCache(cachedKey, JSON.stringify(cacheCollect))
          })
      } else {
        setCache(cachedKey, JSON.stringify(cacheCollect))
      }
    }

    const updateUserKickStat = (kickNum: number, rightNum: number) => {
      const cached = getCacheLong(USER_INFO_KEY)
      if (cached.length > 0) {
        const d = JSON.parse(cached)
        if (checkUserUpdateExpire(d.time)) {
          const usrInfo: UserInfo = d.data
          const curAcc = Math.floor(rightNum / kickNum * 100)
          const preAcc = usrInfo.acc
          usrInfo.acc = Math.floor((preAcc * usrInfo.kickNumber + curAcc * kickNum) / (kickNum + usrInfo.kickNumber))
          usrInfo.kickNumber += kickNum
          const curKickRatio = Math.floor(usrInfo.kickNumber / usrInfo.totalQusNum * 100)
          usrInfo.kickProgress = curKickRatio >= 100 ? 100 : curKickRatio
          const cachedData = {
            data: usrInfo,
            time: d.time
          }
          setCacheLong(USER_INFO_KEY, JSON.stringify(cachedData))
        }
      }
    }

    const submitKillRecord = () => {
      // setCacheLong(CACHED_CHOICE, JSON.stringify(cachedChoice.value))
      const token:string = checkToken()
      if (token === '') {
        return
      }
      const uid = decodeID(token)
      const h = killRecord.value
      const data: SolveLog[] = []
      let kickNum = 0
      let rightNum = 0
      if (h.size > 0) {
        h.forEach(v => {
          data.push({
            subject: subject.value.code,
            uid: uid,
            pid: v.pid,
            status: v.status,
            created: v.created
          })
          kickNum += 1
          if (v.status === 1) {
            rightNum += 1
          }
        })

        updateUserKickStat(kickNum, rightNum)

        addRecordLog(data)
          .then((response) => {
            if (response.status !== 200) {
              return
            }
            const resp = response.data
            if (resp.code === 0) {
              h.clear()
            }
          })
          .catch((err) => {
            console.log(err)
          })
      }
    }

    const submitDataOnLeave = () => {
      submitKillRecord()

      leaveSubmitCollect()

      submitBreakpoint()

      submitHistoryChoice()
    }

    const getCurrentQusPid = (curCateIdx: number, curQusIdx: number):number => {
      const sub = subject.value
      if (curCateIdx >= 0 && curCateIdx < sub.category.length) {
        const targetCate = sub.category[curCateIdx]
        const group = Math.floor(curQusIdx / loadedQusBatchSize)
        const index = curQusIdx % loadedQusBatchSize
        if (targetCate.ids !== null && targetCate.ids.has(group)) {
          const ids = targetCate.ids.get(group)
          if (ids !== undefined && index < ids.data.length) {
            return ids.data[index]
          }
        }
      }
      return 0
    }

    const submitBreakpoint = () => {
      const subjectCode = subject.value.code
      if (subjectCode === 'FUN') {
        return
      }
      const i = cateIdx.value
      if (i < 0 || killRecord.value.size === 0) {
        return
      }

      const qid = getCurrentQusPid(cateIdx.value, qusIdx.value)
      const breakpoint: Breakpoint = {
        pid: qid,
        idx: qusIdx.value,
        subject: subjectCode,
        category: curCate.value
      }

      addBreakpoint(breakpoint)

      const bk = getCache(BREAKPOINT_KEY)
      if (bk.length === 0) {
        const d: Breakpoint[] = []
        d.push(breakpoint)
        setCache(BREAKPOINT_KEY, JSON.stringify(d))
      } else {
        const d = JSON.parse(bk) as Breakpoint[]
        const subIdx = d.findIndex(v => v.subject === subjectCode)
        if (subIdx !== -1) {
          d[subIdx].pid = breakpoint.pid
          d[subIdx].idx = breakpoint.idx
          d[subIdx].subject = breakpoint.subject
          d[subIdx].category = breakpoint.category
        } else {
          d.push(breakpoint)
        }
        setCache(BREAKPOINT_KEY, JSON.stringify(d))
      }
    }

    const onChoiceChange = (e: Event, pid: number) => {
      const c = curChoice.value
      if (c !== '') {
        // 新的做题记录 status = 2
        let status = 2
        if (cachedChoice.value.has(pid)) {
          // 更新做题记录 status = 1
          status = 1
        }
        const choice: ChoicePair = {
          choice: c,
          created: new Date().getTime(),
          status: status
        }
        cachedChoice.value.set(pid, choice)
      }
    }

    const submitHistoryChoice = () => {
      const sub = subject.value.code
      if (sub.length === 0 || sub === 'FUN') {
        return
      }
      const e = cachedChoice.value
      const dataSend: HistoryAnswer[] = []
      const cachedString: string[] = []
      e.forEach((value, key) => {
        cachedString.push(`${key}=${value.choice}`)
        if (value.status > 0) {
          const u: HistoryAnswer = {
            id: 0,
            uid: 0,
            pid: key,
            choice: value.choice,
            created: value.created
          }
          dataSend.push(u)
        }
      })
      if (dataSend.length > 0) {
        const subCateCode = getSubjectCategoryCode(sub)
        setUserHistoryAnswer(subCateCode.toString(), dataSend)
          .then(response => {
            if (response.status !== 200) {
              return
            }
            const resp = response.data
            if (resp.code !== 0 || resp.data === null) {
              return
            }
            cachedChoice.value.clear()
            setCache(`${CACHED_CHOICE}_${sub}`, cachedString.join('#'))
          })
      }
    }

    const initHistoryAnswer = () => {
      const sub = subject.value.code
      if (sub === '' || sub === 'FUN') {
        return
      }
      const cacheKey = `${CACHED_CHOICE}_${sub}`
      const cached = getCache(cacheKey)
      if (cached.length > 0) {
        cachedChoice.value.clear()
        const e = cached.split('#')
        e.forEach(v => {
          const u = v.split('=')
          const pid = parseInt(u[0])
          const c: ChoicePair = {
            choice: u[1],
            created: 0,
            status: 0
          }
          cachedChoice.value.set(pid, c)
        })
        return
      }
      const subCateCode = getSubjectCategoryCode(sub)
      getUserHistoryAnswer(subCateCode.toString())
        .then(response => {
          if (response.status !== 200) {
            return
          }
          const resp = response.data
          if (resp.code !== 0 || resp.data === null) {
            return
          }
          cachedChoice.value.clear()
          const dataCache: string[] = []
          for (const e in resp.data) {
            const pid = parseInt(e)
            const f = resp.data[e].split(',')
            dataCache.push(`${pid}=${f[0]}`)
            const w: ChoicePair = {
              status: 0,
              created: 0,
              choice: f[0]
            }
            cachedChoice.value.set(pid, w)
            setCache(cacheKey, dataCache.join('#'))
          }
        })
    }

    const goBack = () => {
      router.back()
    }

    onMounted(() => {
      // load user collection
      const targetCate = route.params.cate as string
      message.info('正在加载数据...', 0.2)
      setupPlayground(targetCate)

      uname.value = getUserName()

      // addListener
      window.addEventListener('beforeunload', submitDataOnLeave)

      // load user Collection
      loadUserCollect(targetCate)

      // reset history choice
      initHistoryAnswer()
    })

    watch(curChoice, (curChoice) => {
      if (!enabledShowAns.value && curChoice !== '') {
        enabledShowAns.value = true
      }
    })

    onBeforeUnmount(() => {
      submitDataOnLeave()
    })

    onUnmounted(() => {
      window.removeEventListener('beforeunload', submitDataOnLeave)
    })

    return {
      commentPad,
      uname,
      subject,
      preCate,
      curCate,
      postCate,
      cateIdx,
      cateQusNum,
      collectStatus,
      qus,
      qusIdx,
      curChoice,
      isShowAns,
      enabledShowAns,
      ansResultColor,
      switchLoading,
      showCollect,
      goBack,
      switchCate,
      addCollect,
      switchQus,
      showAns,
      onChoiceChange
    }
  }
})
</script>

<style scoped>
.header {
  padding: 20px 0 20px 0;
}
.header .subject {
  height: 60px;
  width: 240px;
  color: #ffffff;
  background-color: #3498db;
  clip-path: polygon(0 0, 100% 0, 85% 50%, 100% 100%, 0 100%, 15% 50%);
  padding: 10px;
  text-align: center;
  margin: auto;
}
.header .subject span {
  font-size: 2em;
}

.navi {
  margin-top: 25px;
}

.navi .sub {
  text-align: center;
}

.navi .sub span {
  cursor: pointer;
  display: inline-block;
  margin: 0 5px;
}

.navi .not-select span{
  border-bottom: 2px solid #aeb8c2;
  font-size: 1em;
}

.navi .selected span{
  font-size: 1.2em;
  padding: 2px 8px;
  border-radius: 10px;
  color: #ffffff;
  background-color: #1abc9c;
}

.problem-pad {
  margin-top: 10px;
}

.problem-pad .problem {
  border-radius: 10px;
  padding: 0 15px;
  box-shadow: rgba(0, 0, 0, 0.25) 0 3px 8px;
  user-select: none;
}

.problem-pad .pad-header {
  margin-bottom: 18px;
}

.problem-pad .pad-header span{
  display: inline-block;
  font-size: 1.3em;
  padding: 0 5px;
  cursor: pointer;
}

.problem-pad .pad-header .op-color {
  background-color: #1abc9c;
}

.problem-pad .pad-header .disabled {
  background-color:#6f6e6e
}

.problem-pad .pad-header .op {
  margin-left: 10px;
  border-radius: 8px;
  color: white;
}

.problem-pad .problem .qus-info span {
  display: inline-block;
  font-size: 1.2em;
  margin-top: 5px;
}

.problem-pad .problem .qus-info span:not(:first-child) {
  margin-left: 10px;
}

.problem-pad .problem .choice {
  padding: 20px 0 10px 0;
}

.problem-pad .answer {
  border-radius: 10px;
  padding: 0 15px;
  box-shadow: rgba(170, 63, 63, 0.25) 0 3px 8px;
  margin: 25px 0;
  background-color: #f3f3f3;
  user-select: none;
}

.problem-pad .answer .provider {
  padding-top: 10px;
}
.problem-pad .answer .provider span{
  font-size: 1.2em;
  padding: 2px;
  font-weight: bold;
  letter-spacing: 1px;
}

.footer {
  text-align: center;
  margin-top: 10px;
  margin-bottom: 5px;
  padding: 5px;
}
.footer span {
  display: inline-block;
  font-weight: 400;
}

.go-back {
  z-index: 999;
  cursor: pointer;
  font-size: 20px;
  margin-bottom: 20px;
  margin-top: 10px;
}
</style>
